package org.zn.note.common.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

/**
 * @ClassName Base64Util
 * @Author zn
 * @Date 2019/5/15 11:33
 **/
/*
性能测试可以看 Base64UtilTest： jdk1.8 > apache > sun

字节流(byte)怎么保存成“可见”的内容？ （用于网络传输或保存到数据库）
（比如以前发邮件只能传送可见字符，需要将不可见字符转换成可见字符传输；
或者图片的二进制流想存成可见字符；当然，都放大了存储）
源数据：String：0123456789 (10 bytes)  【真实字节 0x30 0x31 ... 0x39】
1、转换成Hex：30313233343536373839 (20 bytes)  长度固定增加1倍
2、Base64：MDEyMzQ1Njc4OQ== (16 bytes)  长度大概增加1/3 (3个字节分组转换成4字节)

Base64算法举例
1、3个字节分组
0x30 0x31 0x32 、0x33 0x34 0x35 、0x36 0x37 0x38 、0x39
组：00110000、00110001、00110010、
组：00110011、00110100、00110101、
组：00110110、00110111、00111000、
组：00111001
2、每组变成4个字节
（原来每个字节8bit，现在按6bit分，这样分出的4个字节，然后每个字节前面补2bit 0）
组：001100、000011、000100、110010 --> 00001100、00000011、00000100、00110010
                                   --> 12、3、4、50 --> M、D、E、y
组：001100、110011、010000、110101 --> 00001100、00110011、00010000、00110101
                                   --> 12、51、16、53 --> M、z、Q、1
组：001101、100011、011100、111000 --> 00001101、00100011、00011100、00111000
                                   --> 13、35、28、56 --> N、j、c、4
组：001110、010000(这里后面自动补0)--> 00001110、00010000
                                   --> 14、16 --> O、Q
每组要求3字节，最后这组只有1个字节，所以要补2个=
最终：string（0123456789） ----base64----> string（MDEyMzQ1Njc4OQ==）

为什么选择6bit分组，如果7bit分组不能更能压缩吗？
当6bit分组时，范围 00000000 - 00111111，0~63
（注意！Base64的可见字符映射，不是ascii ！ 是自定义的！
00~25 A-Z    26~51 a-z    52~61 0-9    62 +    63 /    占位符 =
）
如果7bit分组，范围 00000000 - 01111111，0~127，没有这么多可见字符！
（由此可见，Base64是转换成可见字符的最大压缩算法）

附：ASCII（American Standard Code for Information Interchang）
（用一个字节的低7bit，表示出 128个不同含义。最高位bit用于奇偶校验）
不可见：33个
0~31 控制符和制表符、127 控制符(DEL)
可见的：95个
32 空格、33~47 符号、48~57 数字、58~64 符号、
65~90 大写字母、91~96 符号、97~122 小写字母、123~126 符号
 */
public class Base64Util {

    /**
     * base64 编码
     * jdk 1.8之前，sun提供的 base64工具
     *
     * @param content
     */
    public static String base64EncodeBySunBefor18(byte[] content)
            throws UnsupportedEncodingException {
        final sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
        return encoder.encode(content);
    }

    /**
     * base64 解码
     * jdk 1.8之前，sun提供的 base64工具
     *
     * @param content
     */
    public static byte[] base64DecodeBySunBefor18(String content)
            throws IOException {
        final sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
        return decoder.decodeBuffer(content);
    }

    /**
     * base64 编码
     * Apache Commons Codec 提供的方法
     *
     * @param content
     * @return
     */
    public static String base64EncodeByApacheCommons(byte[] content) {
        final org.apache.commons.codec.binary.Base64 base64 =
                new org.apache.commons.codec.binary.Base64();
        return base64.encodeToString(content);
    }

    /**
     * base64 解码
     * Apache Commons Codec 提供的方法
     *
     * @param content
     * @return
     */
    public static byte[] base64DecodeByApacheCommons(String content) {
        final org.apache.commons.codec.binary.Base64 base64 =
                new org.apache.commons.codec.binary.Base64();
        return base64.decode(content);
    }

    /**
     * base64 编码
     * jdk 1.8 新增加的 java.util.Base64 工具类
     *
     * @param content
     */
    public static String base64EncodeByJdkAfter18(byte[] content) {
        final java.util.Base64.Encoder encoder = java.util.Base64.getEncoder();
        return encoder.encodeToString(content);
    }

    /**
     * base64 解码
     * jdk 1.8 新增加的 java.util.Base64 工具类
     *
     * @param content
     */
    public static byte[] base64DecodeByJdkAfter18(String content) {
        final java.util.Base64.Decoder decoder = java.util.Base64.getDecoder();
        return decoder.decode(content);
    }
}
